/*
 * Copyright 2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.r2dbc.postgresql.codec;

import io.netty.buffer.ByteBuf;
import io.r2dbc.postgresql.client.EncodedParameter;
import io.r2dbc.postgresql.client.ParameterAssert;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.time.Period;

import static io.r2dbc.postgresql.client.EncodedParameter.NULL_VALUE;
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INTERVAL;
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.VARCHAR;
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT;
import static io.r2dbc.postgresql.util.ByteBufUtils.encode;
import static io.r2dbc.postgresql.util.TestByteBufAllocator.TEST;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;

/**
 * Unit tests for {@link IntervalCodec}.
 */
final class IntervalCodecUnitTests {

    private static final int dataType = INTERVAL.getObjectId();

    @Test
    void constructorNoByteBufAllocator() {
        assertThatIllegalArgumentException().isThrownBy(() -> new IntervalCodec(null))
            .withMessage("byteBufAllocator must not be null");
    }

    @Test
    void doCanDecodeNoType() {
        assertThatIllegalArgumentException().isThrownBy(() -> new IntervalCodec(TEST).doCanDecode(null, FORMAT_TEXT))
            .withMessage("type must not be null");
    }

    @Test
    void doCanDecodeNoFormat() {
        assertThatIllegalArgumentException().isThrownBy(() -> new IntervalCodec(TEST).doCanDecode(INTERVAL, null))
            .withMessage("format must not be null");
    }

    @Test
    void doCanDecode() {
        IntervalCodec codec = new IntervalCodec(TEST);

        assertThat(codec.doCanDecode(VARCHAR, FORMAT_TEXT)).isFalse();
        assertThat(codec.doCanDecode(VARCHAR, FORMAT_BINARY)).isFalse();
        assertThat(codec.doCanDecode(INTERVAL, FORMAT_TEXT)).isTrue();
        assertThat(codec.doCanDecode(INTERVAL, FORMAT_BINARY)).isFalse();
    }

    @Test
    void doDecode() {
        IntervalCodec codec = new IntervalCodec(TEST);
        Interval interval = Interval.of(Period.of(20, 5, 8));
        ByteBuf intervalAsText = encode(TEST, "20 year 5 mon 8 day");
        IntervalAssert.assertThat(codec.doDecode(intervalAsText, INTERVAL, FORMAT_TEXT, Interval.class))
            .isEqualTo(interval);
    }

    @Test
    void doEncodeNoValue() {
        assertThatIllegalArgumentException().isThrownBy(() -> new IntervalCodec(TEST).doEncode(null))
            .withMessage("value must not be null");
    }

    @Test
    void doEncode() {
        IntervalCodec codec = new IntervalCodec(TEST);

        ByteBuf intervalAsText = encode(TEST, "10 years 5 mons 2 days 0 hours 0 mins 0.0 secs");
        ParameterAssert.assertThat(codec.doEncode(Interval.of(Period.of(10, 5, 2))))
            .hasFormat(FORMAT_TEXT)
            .hasType(INTERVAL.getObjectId())
            .hasValue(intervalAsText);

        intervalAsText = encode(TEST, "0 years 0 mons 23 days 23 hours 59 mins 3.35 secs");
        ParameterAssert.assertThat(codec.doEncode(Interval.of(Duration.ofSeconds(2073543, 350000000))))
            .hasFormat(FORMAT_TEXT)
            .hasType(INTERVAL.getObjectId())
            .hasValue(intervalAsText);

        intervalAsText = encode(TEST, "0 years 0 mons 0 days 0 hours 0 mins 0.000001 secs");
        ParameterAssert.assertThat(codec.doEncode(Interval.of(Duration.ofNanos(1000))))
            .hasFormat(FORMAT_TEXT)
            .hasType(INTERVAL.getObjectId())
            .hasValue(intervalAsText);
    }

    @Test
    void encodeNull() {
        ParameterAssert.assertThat(new IntervalCodec(TEST).encodeNull())
            .isEqualTo(new EncodedParameter(FORMAT_TEXT, dataType, NULL_VALUE));
    }

}
